﻿namespace UsefulTools.Wpf
{
    using System;
    using System.ComponentModel;
    using System.Linq.Expressions;
    using System.Windows;
    using UsefulTools.Core;
    using UsefulTools.ExtensionMethods;

    public sealed class NotifyChangedObservable<T> : IObservable<T>, IWeakEventListener, IDisposable
    {
        private readonly INotifyPropertyChanged _source;

        private readonly string _propertyName;

        private IObserver<T> _observer;

        private readonly IPropertyAccessor _propertyAccessor;

        public NotifyChangedObservable(INotifyPropertyChanged source, string propertyName)
        {
            _source = source;
            _propertyName = propertyName;
            var propertyInfo = _source.GetType().GetProperty(propertyName);
            _propertyAccessor = PropertyInfoHelper.GetFastAccessor(propertyInfo);
        }

        public IDisposable Subscribe(IObserver<T> observer)
        {
            _observer = observer;
            PropertyChangedEventManager.AddListener(_source, this, _propertyName);
            return this;
        }

        public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            T value = (T)_propertyAccessor.GetValue(_source);
            _observer.OnNext(value);
            return true;
        }

        public void Dispose()
        {
            PropertyChangedEventManager.RemoveListener(_source, this, _propertyName);
            _observer.OnCompleted();
        }
    }

    public static class Mixin
    {
        public static NotifyChangedObservable<T> WhenPropertyChanged<Param, T>(this Param @this,
            Expression<Func<Param,T>> propertyExpression) where Param : ModelElement
        {
            return new NotifyChangedObservable<T>(@this, propertyExpression.PropertyName());
        }
    }

    
}